const { randomUUID } = require('crypto');

class MemoryStore {
  constructor() {
    this.documents = new Map();
  }

  async init() {
    return true;
  }

  generateId() {
    return randomUUID();
  }

  async create(doc) {
    return this.upsert(doc);
  }

  async update(id, updates) {
    const existing = this.documents.get(id);
    if (!existing) return null;
    return this.upsert({ ...existing, ...updates, id });
  }

  async upsert(doc) {
    const id = doc.id || this.generateId();
    const existing = this.documents.get(id) || {};
    const now = new Date().toISOString();
    const next = {
      ...existing,
      ...doc,
      id,
      updatedAt: now,
      createdAt: existing.createdAt || now
    };
    this.documents.set(id, next);
    return next;
  }

  async findById(id) {
    return this.documents.get(id) || null;
  }

  async remove(id) {
    const existed = this.documents.has(id);
    this.documents.delete(id);
    return existed;
  }

  async list({ limit = 50, offset = 0 } = {}) {
    const items = Array.from(this.documents.values()).sort((a, b) => new Date(b.createdAt) - new Date(a.createdAt));
    return items.slice(offset, offset + limit);
  }

  async search({ query, tags = [], limit = 20 }) {
    const q = (query || '').toLowerCase();
    const filtered = Array.from(this.documents.values()).filter((doc) => {
      const matchesQuery = !q ||
        doc.title?.toLowerCase().includes(q) ||
        doc.content?.toLowerCase().includes(q);
      const matchesTags = !tags.length || tags.every((tag) => (doc.metadata?.tags || []).includes(tag));
      return matchesQuery && matchesTags;
    });
    return filtered.slice(0, limit);
  }

  async updateVector(id, vector) {
    const existing = this.documents.get(id);
    if (!existing) return null;
    return this.upsert({ ...existing, vector });
  }

  async searchByVector(vector, limit = 20) {
    if (!Array.isArray(vector)) return [];
    const scored = Array.from(this.documents.values())
      .filter((doc) => Array.isArray(doc.vector))
      .map((doc) => ({ doc, score: cosineSimilarity(vector, doc.vector) }))
      .sort((a, b) => b.score - a.score)
      .slice(0, limit);
    return scored.map(({ doc, score }) => ({ ...doc, score }));
  }
}

function cosineSimilarity(vecA, vecB) {
  if (!vecA || !vecB || vecA.length !== vecB.length) return 0;
  let dot = 0;
  let normA = 0;
  let normB = 0;
  for (let i = 0; i < vecA.length; i++) {
    dot += vecA[i] * vecB[i];
    normA += vecA[i] * vecA[i];
    normB += vecB[i] * vecB[i];
  }
  if (normA === 0 || normB === 0) return 0;
  return dot / (Math.sqrt(normA) * Math.sqrt(normB));
}

const memoryStore = new MemoryStore();

module.exports = { MemoryStore, memoryStore };
